package org.midy.infrastructure;

import org.midy.infrastructure.tools.Logger;

import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 让一个SocketChannel 在一个Selector上唯一对应一个SelectKey:    同时唯一绑定一个
 */
public class ChannelEventHelper {
    private final SelectableChannel socketChannel;

    private SelectionKey selectionKey; //用来关联写操作

    private final BlockingQueue<ByteBuffer> sendMessageQueue = new LinkedBlockingQueue();

    private int preLength = 8;
    private ByteBuffer headByteBuffer; // 报文头长度默认6位

    private ByteBuffer bodyByteBuffer; // 用来缓存消息

    public ChannelEventHelper(SelectionKey selectionKey) {
        this.selectionKey = selectionKey;
        this.socketChannel = selectionKey.channel();
    }

    public ChannelEventHelper(SelectableChannel socketChannel) {
        this.socketChannel = socketChannel;
    }

    public ByteBuffer getHeadByteBuffer() {
        if(headByteBuffer == null) {
            return ByteBuffer.allocate(preLength);
        } else {
            ByteBuffer tmp = headByteBuffer;
            headByteBuffer = null;
            return tmp;
        }
    }

    public void setHeadByteBuffer(ByteBuffer headByteBuffer) {
        this.headByteBuffer = headByteBuffer;
    }

    public ByteBuffer getBodyByteBuffer() {
        ByteBuffer currBodyByteBuffer = this.bodyByteBuffer;
        this.bodyByteBuffer = null;
        return currBodyByteBuffer;
    }

    public void recordBodyByteBuffer(ByteBuffer bodyByteBuffer) {
        this.bodyByteBuffer = bodyByteBuffer;
    }

    public static ChannelEventHelper attachment(SelectionKey selectionKey) {
        ChannelEventHelper attachment = ( ChannelEventHelper) selectionKey.attachment();
        if(attachment == null) {
            attachment = new ChannelEventHelper(selectionKey);
            selectionKey.attach(attachment);
        }
        return attachment;
    }

    public static ChannelEventHelper buildAttachment(SelectableChannel socketChannel) {
        return new ChannelEventHelper(socketChannel);
    }

    public void putSendByteBuffer(ByteBuffer sendMsg) throws InterruptedException {
        sendMessageQueue.put(sendMsg);
    }

    public void closeChannel() {
        if(socketChannel.isOpen()) {
            try {
                socketChannel.close();
            } catch (Throwable t) {
                Logger.error("关闭channel失败", t);
            }
        }
    }

    /**
     * 关闭当前通道的读取等操作，清空缓存
     */
    public void close() {
        closeChannel();
        sendMessageQueue.clear();
        headByteBuffer = null;
        bodyByteBuffer = null;
        selectionKey.cancel();
    }

    public <T> T getSocketChannel() {
        return (T) socketChannel;
    }

    public int getPreLength() {
        return preLength;
    }

    public BlockingQueue<ByteBuffer> getSendMessageQueue() {
        return sendMessageQueue;
    }

    public SelectionKey getSelectionKey() {
        return selectionKey;
    }

    public void setPreLength(int preLength) {
        this.preLength = preLength;
    }

    public void setSelectionKey(SelectionKey selectionKey) {
        this.selectionKey = selectionKey;
    }
}
